#include "util.h"
#include "project.h"
#include "constants.h"

#include "Poco/String.h"
#include "Poco/StringTokenizer.h"
#include "Poco/RegularExpression.h"
#include "Poco/Path.h"
#include "Poco/File.h"
#include "Poco/Glob.h"

#include <iostream>
#include <fstream>
#include <algorithm>

#include "stdio.h"


using namespace std;
using namespace cpgf;

namespace metagen {


std::string normalizeSymbolName(const std::string & symbol)
{
	static Poco::RegularExpression regexp("\\W+");
	string result = symbol;
	
	regexp.subst(result, "_", Poco::RegularExpression::RE_GLOBAL);
	
	Poco::replaceInPlace(result, "__", "_");
	
	return result;
}

std::string normalizeFile(const std::string & file)
{
	std::string result(file);
	std::replace(result.begin(), result.end(), '\\', '/');
	return result;
}

std::string normalizePath(const std::string & path)
{
	string result(normalizeFile(path));
	if(! result.empty() && result[result.size() - 1] != '/') {
		result.append("/");
	}
	return result;
}

std::string makeRelativePath(const std::string & sbase, const std::string & spath)
{
	Poco::Path base(sbase);
	Poco::Path path(spath);

	if(base.getNode() != path.getNode()) {
		return spath;
	}
	if(base.getDevice() != path.getDevice()) {
		return spath;
	}

	int baseDepth = base.depth();
	int pathDepth = path.depth();
	if(baseDepth == 0 || pathDepth == 0) {
		return spath;
	}
	
	int i = 0;
	while(base[i] == path[i]) {
		++i;
		if(i == baseDepth || i == pathDepth) {
			break;
		}
	}
	
	if(i == baseDepth && i == pathDepth) {
		return "";
	}

	Poco::Path result;
	for(int k = i; k < baseDepth; ++k) {
		result.pushDirectory("..");
	}
	for(int k = i; k < pathDepth; ++k) {
		result.pushDirectory(path[k]);
	}

	return result.toString();
}

bool readStringFromFile(const std::string & fileName, std::string * outContent)
{
	FILE * file = fopen(fileName.c_str(), "rb");
	if(file == NULL) {
		return false;
	}
	fseek(file, 0, SEEK_END);
	size_t length = ftell(file);
	fseek(file, 0, SEEK_SET);
	outContent->resize(length);
	fread(&(*outContent)[0], length, 1, file);
	fclose(file);
	return true;
}

bool writeStringToFile(const std::string & fileName, const std::string & content)
{
	FILE * file = fopen(fileName.c_str(), "wb");
	if(file == NULL) {
		return false;
	}
	fwrite(&content[0], content.size(), 1, file);
	fclose(file);
	return true;
}

bool shouldTargetFileBeUpdated(const std::string & sourceFileName, const std::string & targetFileName)
{
	Poco::File targetFile(targetFileName);

	if(! targetFile.exists()) {
		return true;
	}

	Poco::File sourceFile(sourceFileName);
	
	return sourceFile.getLastModified() >= targetFile.getLastModified();
}

bool isFileContentSameToString(const std::string & fileName, const std::string & s)
{
	string content;
	readStringFromFile(fileName, &content);
	return s == content;
}

bool isFileAutoGenerated(const std::string & fileName)
{
	ifstream file(fileName.c_str());
	string line;
	if(getline(file, line)) {
		if(line == GeneratedFileMark) {
			return true;
		}
	}

	return false;
}

void globFiles(const std::string & path, const std::string & pattern, const cpgf::GCallback<void (const std::string &)> & callback)
{
	Poco::Path p(normalizePath(path));
	p.setFileName(pattern);
	set<string> files;
	Poco::Glob::glob(p.toString(), files, Poco::Glob::GLOB_DOT_SPECIAL);
	for(set<string>::iterator it = files.begin(); it != files.end(); ++it) {
		if(Poco::Path(*it).isDirectory()) {
			globFiles(*it, pattern, callback);
		}
		else {
			callback(*it);
		}
	}
}

void splitFileNames(const std::string & fileNames, std::vector<std::string> * splittedNames)
{
	Poco::StringTokenizer tokenizer(fileNames, ";");
	std::copy(tokenizer.begin(), tokenizer.end(), std::back_inserter(*splittedNames));
}

void splitFileNames(const std::vector<std::string> & fileNames, std::vector<std::string> * splittedNames)
{
	for(std::vector<std::string>::const_iterator it = fileNames.begin(); it != fileNames.end(); ++it) {
		splitFileNames(*it, splittedNames);
	}
}

void appendFileNames(std::vector<std::string> * toFileNames, const std::string & fileNames)
{
	std::vector<std::string> splittedNames;
	splitFileNames(fileNames, &splittedNames);
	toFileNames->insert(toFileNames->end(), splittedNames.begin(), splittedNames.end());
}

void appendFileNames(std::vector<std::string> * toFileNames, const std::vector<std::string> & fileNames)
{
	std::vector<std::string> splittedNames;
	splitFileNames(fileNames, &splittedNames);
	toFileNames->insert(toFileNames->end(), splittedNames.begin(), splittedNames.end());
}


} // namespace metagen
